"""
MCPStore API - Agent-level routes
Contains all Agent-level API endpoints
"""

import logging
from typing import Dict, Any, Union, List, Optional

from fastapi import APIRouter, HTTPException, Depends, Request, Query

from mcpstore import MCPStore
from mcpstore.core.models import ResponseBuilder, ErrorCode, timed_response
from mcpstore.core.models.common import APIResponse  # Keep for response_model
from .api_decorators import handle_exceptions, get_store, validate_agent_id
from .api_models import (
    ToolExecutionRecordResponse, ToolRecordsResponse, ToolRecordsSummaryResponse,
    SimpleToolExecutionRequest, create_enhanced_pagination_info
)

# Create Agent-level router
agent_router = APIRouter()

logger = logging.getLogger(__name__)

# === Agent-level operations ===
@agent_router.post("/for_agent/{agent_id}/add_service", response_model=APIResponse)
@timed_response
async def agent_add_service(
    agent_id: str,
    payload: Union[List[str], Dict[str, Any]]
):
    """Add service at agent level"""
    validate_agent_id(agent_id)
    store = get_store()
    context = store.for_agent(agent_id)
    
    # Manually aggregate details after calling add_service
    try:
        await context.add_service_async(payload)
        
        # Aggregate detailed information
        services = context.list_services()
        tools = context.list_tools()
        
        result = {
            "success": True,
            "message": f"Service added successfully for agent '{agent_id}'",
            "added_services": [s.get("name") if isinstance(s, dict) else getattr(s, "name", "unknown") for s in services],
            "total_services": len(services),
            "total_tools": len(tools)
        }
        
        return ResponseBuilder.success(
            message=result["message"],
            data=result
        )
    except Exception as e:
        return ResponseBuilder.error(
            code=ErrorCode.SERVICE_INITIALIZATION_FAILED,
            message=f"Service operation failed for agent '{agent_id}': {str(e)}",
            details={"error": str(e)}
        )

@agent_router.get("/for_agent/{agent_id}/list_services", response_model=APIResponse)
@timed_response
async def agent_list_services(
    agent_id: str,
    # Pagination parameters (optional)
    page: Optional[int] = Query(None, ge=1, description="Page number starting from 1. No pagination when omitted."),
    limit: Optional[int] = Query(None, ge=1, le=1000, description="Items per page. No pagination when omitted."),
    # Filter parameters (optional)
    status: Optional[str] = Query(None, description="Filter by status (e.g., healthy, initializing, error)"),
    search: Optional[str] = Query(None, description="Search by service name (fuzzy match)"),
    service_type: Optional[str] = Query(None, description="Filter by service type (e.g., sse, stdio)"),
    # Sort parameters (optional)
    sort_by: Optional[str] = Query(None, description="Sort field (name, status, type, tools_count)"),
    sort_order: Optional[str] = Query(None, description="Sort direction (asc, desc)")
):
    """
    Get service list at agent level (supports pagination/filtering/sorting)

    Features:
    - All parameters are optional, returns all data when no parameters provided
    - Supports filtering by status, name, type
    - Supports sorting by multiple fields
    - Unified response format, always includes pagination field

    Examples:
    - Get all: GET /for_agent/agent1/list_services
    - Pagination: GET /for_agent/agent1/list_services?page=1&limit=10
    - Filter: GET /for_agent/agent1/list_services?status=healthy&service_type=sse
    - Search: GET /for_agent/agent1/list_services?search=weather
    - Sort: GET /for_agent/agent1/list_services?sort_by=name&sort_order=asc
    - Combined: GET /for_agent/agent1/list_services?status=healthy&page=1&limit=10&sort_by=tools_count&sort_order=desc
    """
    validate_agent_id(agent_id)
    store = get_store()
    context = store.for_agent(agent_id)

    # 1. Get all services
    all_services = await context.list_services_async()

    # 2. Build complete service data
    services_data = []
    for service in all_services:
        service_data = {
            "name": service.name,
            "url": service.url or "",
            "command": service.command or "",
            "args": service.args or [],
            "env": service.env or {},
            "working_dir": service.working_dir or "",
            "package_name": service.package_name or "",
            "keep_alive": service.keep_alive,
            "type": service.transport_type.value if service.transport_type else 'unknown',
            "status": service.status.value if hasattr(service.status, 'value') else str(service.status),
            "tools_count": getattr(service, 'tool_count', 0),
            "client_id": service.client_id or "",
            "config": service.config or {}
        }
        services_data.append(service_data)

    # 3. Apply filtering
    filtered = services_data
    applied_filters = {}

    if status:
        filtered = [s for s in filtered if s.get("status", "").lower() == status.lower()]
        applied_filters["status"] = status

    if search:
        search_lower = search.lower()
        filtered = [s for s in filtered if search_lower in s.get("name", "").lower()]
        applied_filters["search"] = search

    if service_type:
        filtered = [s for s in filtered if s.get("type", "").lower() == service_type.lower()]
        applied_filters["service_type"] = service_type

    # 4. Apply sorting
    applied_sort = {}
    if sort_by:
        reverse = (sort_order == "desc")
        if sort_by == "name":
            filtered.sort(key=lambda s: s.get("name", ""), reverse=reverse)
        elif sort_by == "status":
            filtered.sort(key=lambda s: s.get("status", ""), reverse=reverse)
        elif sort_by == "type":
            filtered.sort(key=lambda s: s.get("type", ""), reverse=reverse)
        elif sort_by == "tools_count":
            filtered.sort(key=lambda s: s.get("tools_count", 0), reverse=reverse)

        applied_sort = {"by": sort_by, "order": sort_order or "asc"}

    filtered_count = len(filtered)

    # 5. Apply pagination
    if page is not None or limit is not None:
        # Paginate only when pagination parameters are provided
        page = page or 1
        limit = limit or 20
        start = (page - 1) * limit
        paginated = filtered[start:start + limit]
    else:
        # Return all data when no pagination parameters
        paginated = filtered

    # 6. Build unified response format (always includes pagination field)
    pagination = create_enhanced_pagination_info(page, limit, filtered_count)

    response_data = {
        "services": paginated,
        "pagination": pagination.dict()
    }

    # Add filter and sort information (if applied)
    if applied_filters:
        response_data["filters"] = applied_filters
    if applied_sort:
        response_data["sort"] = applied_sort

    return ResponseBuilder.success(
        message=f"Retrieved {len(paginated)} of {filtered_count} services for agent '{agent_id}'",
        data=response_data
    )

@agent_router.get("/for_agent/{agent_id}/summary", response_model=APIResponse)
@timed_response
async def agent_summary(agent_id: str):
    """Return agent-level statistical summary (object-oriented entry point wrapper)."""
    validate_agent_id(agent_id)
    store = get_store()
    proxy = store.for_agent_proxy(agent_id)
    stats = proxy.get_stats()
    return ResponseBuilder.success(
        message=f"Agent '{agent_id}' summary returned",
        data=stats
    )

@agent_router.post("/for_agent/{agent_id}/reset_service", response_model=APIResponse)
@timed_response
async def agent_reset_service(agent_id: str, request: Request):
    """Reset service status at agent level"""
    validate_agent_id(agent_id)
    body = await request.json()
    
    store = get_store()
    context = store.for_agent(agent_id)
    
    # Extract parameters
    identifier = body.get("identifier")
    client_id = body.get("client_id")
    service_name = body.get("service_name")

    used_identifier = service_name or identifier or client_id

    if not used_identifier:
        return ResponseBuilder.error(
            code=ErrorCode.VALIDATION_ERROR,
            message="Missing service identifier",
            field="service_name"
        )

    # Call init_service method to reset status
    await context.init_service_async(
        client_id_or_service_name=identifier,
        client_id=client_id,
        service_name=service_name
    )
    
    return ResponseBuilder.success(
        message=f"Service '{used_identifier}' reset successfully for agent '{agent_id}'",
        data={"service_name": used_identifier, "agent_id": agent_id, "status": "initializing"}
    )

@agent_router.get("/for_agent/{agent_id}/list_tools", response_model=APIResponse)
@timed_response
async def agent_list_tools(
    agent_id: str,
    # Pagination parameters (optional)
    page: Optional[int] = Query(None, ge=1, description="Page number starting from 1. No pagination when omitted."),
    limit: Optional[int] = Query(None, ge=1, le=1000, description="Items per page. No pagination when omitted."),
    # Filter parameters (optional)
    search: Optional[str] = Query(None, description="Search by tool name or description (fuzzy match)"),
    service_name: Optional[str] = Query(None, description="Filter by service name (exact match)"),
    # Sort parameters (optional)
    sort_by: Optional[str] = Query(None, description="Sort field (name, service)"),
    sort_order: Optional[str] = Query(None, description="Sort direction (asc, desc)")
):
    """
    Get tool list at agent level (supports pagination/filtering/sorting)

    Features:
    - All parameters are optional, returns all data when no parameters provided
    - Supports filtering by tool name, description, service name
    - Supports sorting by name, service
    - Unified response format, always includes pagination field

    Examples:
    - Get all: GET /for_agent/agent1/list_tools
    - Pagination: GET /for_agent/agent1/list_tools?page=1&limit=20
    - Search: GET /for_agent/agent1/list_tools?search=read
    - By service: GET /for_agent/agent1/list_tools?service_name=filesystem
    - Sort: GET /for_agent/agent1/list_tools?sort_by=name&sort_order=asc
    - Combined: GET /for_agent/agent1/list_tools?service_name=filesystem&page=1&limit=10&sort_by=name
    """
    validate_agent_id(agent_id)
    store = get_store()
    context = store.for_agent(agent_id)

    # 1. Get all tools
    all_tools = context.list_tools()

    # 2. Build tool data
    tools_data = [
        {
            "name": tool.name,
            "service": getattr(tool, 'service_name', 'unknown'),
            "description": tool.description or ""
        }
        for tool in all_tools
    ]

    # 3. Apply filtering
    filtered = tools_data
    applied_filters = {}

    if search:
        search_lower = search.lower()
        filtered = [
            t for t in filtered
            if search_lower in t.get("name", "").lower() or search_lower in t.get("description", "").lower()
        ]
        applied_filters["search"] = search

    if service_name:
        filtered = [t for t in filtered if t.get("service", "") == service_name]
        applied_filters["service_name"] = service_name

    # 4. Apply sorting
    applied_sort = {}
    if sort_by:
        reverse = (sort_order == "desc")
        if sort_by == "name":
            filtered.sort(key=lambda t: t.get("name", ""), reverse=reverse)
        elif sort_by == "service":
            filtered.sort(key=lambda t: t.get("service", ""), reverse=reverse)

        applied_sort = {"by": sort_by, "order": sort_order or "asc"}

    filtered_count = len(filtered)

    # 5. Apply pagination
    if page is not None or limit is not None:
        # Paginate only when pagination parameters are provided
        page = page or 1
        limit = limit or 20
        start = (page - 1) * limit
        paginated = filtered[start:start + limit]
    else:
        # Return all data when no pagination parameters
        paginated = filtered

    # 6. Build unified response format (always includes pagination field)
    pagination = create_enhanced_pagination_info(page, limit, filtered_count)

    response_data = {
        "tools": paginated,
        "pagination": pagination.dict()
    }

    # Add filter and sort information (if applied)
    if applied_filters:
        response_data["filters"] = applied_filters
    if applied_sort:
        response_data["sort"] = applied_sort

    return ResponseBuilder.success(
        message=f"Retrieved {len(paginated)} of {filtered_count} tools for agent '{agent_id}'",
        data=response_data
    )

@agent_router.get("/for_agent/{agent_id}/check_services", response_model=APIResponse)
@timed_response
async def agent_check_services(agent_id: str):
    """Batch health check at agent level"""
    validate_agent_id(agent_id)
    store = get_store()
    context = store.for_agent(agent_id)
    health_status = await context.check_services_async()
    
    return ResponseBuilder.success(
        message=f"Health check completed for agent '{agent_id}'",
        data=health_status
    )

@agent_router.post("/for_agent/{agent_id}/call_tool", response_model=APIResponse)
@timed_response
async def agent_call_tool(agent_id: str, request: SimpleToolExecutionRequest):
    """Tool execution at agent level"""
    validate_agent_id(agent_id)
    
    store = get_store()
    context = store.for_agent(agent_id)
    result = await context.call_tool_async(request.tool_name, request.args)
    
    return ResponseBuilder.success(
        message=f"Tool '{request.tool_name}' executed successfully for agent '{agent_id}'",
        data=result
    )

@agent_router.put("/for_agent/{agent_id}/update_service/{service_name}", response_model=APIResponse)
@timed_response
async def agent_update_service(agent_id: str, service_name: str, request: Request):
    """Update service configuration at agent level"""
    validate_agent_id(agent_id)
    body = await request.json()
    
    store = get_store()
    context = store.for_agent(agent_id)
    result = await context.update_service_async(service_name, body)
    
    if not result:
        return ResponseBuilder.error(
            code=ErrorCode.SERVICE_NOT_FOUND,
            message=f"Failed to update service '{service_name}' for agent '{agent_id}'",
            field="service_name"
        )
    
    return ResponseBuilder.success(
        message=f"Service '{service_name}' updated for agent '{agent_id}'",
        data={"service_name": service_name, "agent_id": agent_id}
    )

@agent_router.delete("/for_agent/{agent_id}/delete_service/{service_name}", response_model=APIResponse)
@timed_response
async def agent_delete_service(agent_id: str, service_name: str):
    """Delete service at agent level"""
    validate_agent_id(agent_id)
    store = get_store()
    context = store.for_agent(agent_id)
    result = await context.delete_service_async(service_name)
    
    if not result:
        return ResponseBuilder.error(
            code=ErrorCode.SERVICE_NOT_FOUND,
            message=f"Failed to delete service '{service_name}' for agent '{agent_id}'",
            field="service_name"
        )
    
    return ResponseBuilder.success(
        message=f"Service '{service_name}' deleted for agent '{agent_id}'",
        data={"service_name": service_name, "agent_id": agent_id}
    )

@agent_router.post("/for_agent/{agent_id}/disconnect_service", response_model=APIResponse)
@timed_response
async def agent_disconnect_service(agent_id: str, request: Request):
    """Disconnect service at agent level (lifecycle disconnection without config modification)

    Body example:
    {
      "service_name": "localName",  # Agent local name
      "reason": "user_requested"
    }
    """
    validate_agent_id(agent_id)
    body = await request.json()
    local_name = body.get("service_name") or body.get("name")
    reason = body.get("reason", "user_requested")

    if not local_name:
        return ResponseBuilder.error(
            code=ErrorCode.VALIDATION_ERROR,
            message="Missing service_name",
            field="service_name"
        )

    store = get_store()
    context = store.for_agent(agent_id)

    try:
        ok = await context.disconnect_service_async(local_name, reason=reason)
        if ok:
            return ResponseBuilder.success(
                message=f"Service '{local_name}' disconnected for agent '{agent_id}'",
                data={"agent_id": agent_id, "service_name": local_name, "status": "disconnected"}
            )
        return ResponseBuilder.error(
            code=ErrorCode.SERVICE_OPERATION_FAILED,
            message=f"Failed to disconnect service '{local_name}' for agent '{agent_id}'",
            details={"agent_id": agent_id, "service_name": local_name}
        )
    except Exception as e:
        return ResponseBuilder.error(
            code=ErrorCode.INTERNAL_ERROR,
            message=f"Failed to disconnect service '{local_name}' for agent '{agent_id}': {e}",
            details={"agent_id": agent_id, "service_name": local_name}
        )

@agent_router.get("/for_agent/{agent_id}/show_mcpconfig", response_model=APIResponse)
@timed_response
async def agent_show_mcpconfig(agent_id: str):
    """Get MCP configuration at agent level"""
    validate_agent_id(agent_id)
    store = get_store()
    context = store.for_agent(agent_id)
    config = context.show_mcpconfig()
    
    return ResponseBuilder.success(
        message=f"MCP configuration retrieved for agent '{agent_id}'",
        data=config
    )

@agent_router.get("/for_agent/{agent_id}/show_config", response_model=APIResponse)
@timed_response
async def agent_show_config(agent_id: str):
    """Display configuration information at agent level"""
    validate_agent_id(agent_id)
    store = get_store()
    config_data = await store.for_agent(agent_id).show_config_async()
    
    # Check for errors
    if "error" in config_data:
        return ResponseBuilder.error(
            code=ErrorCode.CONFIGURATION_ERROR,
            message=config_data["error"],
            details=config_data
        )
    
    return ResponseBuilder.success(
        message=f"Retrieved configuration for agent '{agent_id}'",
        data=config_data
    )

@agent_router.delete("/for_agent/{agent_id}/delete_config/{client_id_or_service_name}", response_model=APIResponse)
@timed_response
async def agent_delete_config(agent_id: str, client_id_or_service_name: str):
    """Delete service configuration at agent level"""
    validate_agent_id(agent_id)
    store = get_store()
    result = await store.for_agent(agent_id).delete_config_async(client_id_or_service_name)
    
    if result.get("success"):
        return ResponseBuilder.success(
            message=result.get("message", "Configuration deleted successfully"),
            data=result
        )
    else:
        return ResponseBuilder.error(
            code=ErrorCode.CONFIGURATION_ERROR,
            message=result.get("error", "Failed to delete configuration"),
            details=result
        )

@agent_router.put("/for_agent/{agent_id}/update_config/{client_id_or_service_name}", response_model=APIResponse)
@timed_response
async def agent_update_config(agent_id: str, client_id_or_service_name: str, new_config: dict):
    """Update service configuration at agent level"""
    validate_agent_id(agent_id)
    store = get_store()
    result = await store.for_agent(agent_id).update_config_async(client_id_or_service_name, new_config)
    
    if result.get("success"):
        return ResponseBuilder.success(
            message=result.get("message", "Configuration updated successfully"),
            data=result
        )
    else:
        return ResponseBuilder.error(
            code=ErrorCode.CONFIGURATION_ERROR,
            message=result.get("error", "Failed to update configuration"),
            details=result
        )

@agent_router.post("/for_agent/{agent_id}/reset_config", response_model=APIResponse)
@timed_response
async def agent_reset_config(agent_id: str):
    """Reset configuration at agent level"""
    validate_agent_id(agent_id)
    store = get_store()
    success = await store.for_agent(agent_id).reset_config_async()
    
    if not success:
        return ResponseBuilder.error(
            code=ErrorCode.CONFIGURATION_ERROR,
            message=f"Failed to reset agent '{agent_id}' configuration",
            field="agent_id"
        )
    
    return ResponseBuilder.success(
        message=f"Agent '{agent_id}' configuration reset successfully",
        data={"agent_id": agent_id, "reset": True}
    )

# === Agent-level Statistics and Monitoring ===

@agent_router.get("/for_agent/{agent_id}/tool_records", response_model=APIResponse)
@timed_response
async def get_agent_tool_records(agent_id: str, limit: int = 50):
    """Get tool execution records at agent level"""
    validate_agent_id(agent_id)
    store = get_store()
    records_data = await store.for_agent(agent_id).get_tool_records_async(limit)
    
    return ResponseBuilder.success(
        message=f"Retrieved {len(records_data.get('executions', []))} tool execution records for agent '{agent_id}'",
        data=records_data
    )

# === Backward Compatibility Routes ===

@agent_router.post("/for_agent/{agent_id}/use_tool", response_model=APIResponse)
async def agent_use_tool(agent_id: str, request: SimpleToolExecutionRequest):
    """Tool execution at agent level - backward compatibility alias

    Recommended to use /for_agent/{agent_id}/call_tool endpoint
    """
    return await agent_call_tool(agent_id, request)

@agent_router.post("/for_agent/{agent_id}/wait_service", response_model=APIResponse)
@timed_response
async def agent_wait_service(agent_id: str, request: Request):
    """Wait for service to reach specified state at agent level"""
    body = await request.json()

    # Extract parameters
    client_id_or_service_name = body.get("client_id_or_service_name")
    if not client_id_or_service_name:
        return ResponseBuilder.error(
            code=ErrorCode.VALIDATION_ERROR,
            message="Missing required parameter: client_id_or_service_name",
            field="client_id_or_service_name"
        )

    status = body.get("status", "healthy")
    timeout = body.get("timeout", 10.0)
    raise_on_timeout = body.get("raise_on_timeout", False)

    # Call SDK
    store = get_store()
    context = store.for_agent(agent_id)
    
    result = await context.wait_service_async(
        client_id_or_service_name=client_id_or_service_name,
        status=status,
        timeout=timeout,
        raise_on_timeout=raise_on_timeout
    )
    
    return ResponseBuilder.success(
        message=f"Service wait {'completed' if result else 'timeout'} for agent '{agent_id}'",
        data={
            "agent_id": agent_id,
            "service": client_id_or_service_name,
            "result": result
        }
    )

@agent_router.post("/for_agent/{agent_id}/restart_service", response_model=APIResponse)
@timed_response
async def agent_restart_service(agent_id: str, request: Request):
    """Restart service at agent level"""
    body = await request.json()

    # Extract parameters
    service_name = body.get("service_name")
    if not service_name:
        return ResponseBuilder.error(
            code=ErrorCode.VALIDATION_ERROR,
            message="Missing required parameter: service_name",
            field="service_name"
        )

    # Call SDK
    store = get_store()
    context = store.for_agent(agent_id)
    
    result = await context.restart_service_async(service_name)
    
    if not result:
        return ResponseBuilder.error(
            code=ErrorCode.SERVICE_OPERATION_FAILED,
            message=f"Failed to restart service '{service_name}' for agent '{agent_id}'",
            field="service_name"
        )
    
    return ResponseBuilder.success(
        message=f"Service '{service_name}' restarted for agent '{agent_id}'",
        data={"agent_id": agent_id, "service_name": service_name, "restarted": True}
    )


# === Agent-level Service Details APIs ===

@agent_router.get("/for_agent/{agent_id}/service_info/{service_name}", response_model=APIResponse)
@timed_response
async def agent_get_service_info_detailed(agent_id: str, service_name: str):
    """Get detailed service information at agent level"""
    validate_agent_id(agent_id)
    store = get_store()
    context = store.for_agent(agent_id)

    # Use SDK to get service information
    info = context.get_service_info(service_name)
    if not info or not getattr(info, 'success', False):
        return ResponseBuilder.error(
            code=ErrorCode.SERVICE_NOT_FOUND,
            message=getattr(info, 'message', f"Service '{service_name}' not found for agent '{agent_id}'"),
            field="service_name"
        )

    # Simplify response structure
    service = getattr(info, 'service', None)
    service_info = {
        "name": service.name,
        "status": service.status.value if hasattr(service.status, 'value') else str(service.status),
        "type": service.transport_type.value if service.transport_type else 'unknown',
        "tools_count": getattr(service, 'tool_count', 0)
    }
    
    return ResponseBuilder.success(
        message=f"Service info retrieved for '{service_name}' in agent '{agent_id}'",
        data=service_info
    )

@agent_router.get("/for_agent/{agent_id}/service_status/{service_name}", response_model=APIResponse)
@timed_response
async def agent_get_service_status(agent_id: str, service_name: str):
    """Get service status at agent level"""
    validate_agent_id(agent_id)
    store = get_store()
    context = store.for_agent(agent_id)

    # Find service
    service = None
    all_services = await context.list_services_async()
    for s in all_services:
        if s.name == service_name:
            service = s
            break

    if not service:
        return ResponseBuilder.error(
            code=ErrorCode.SERVICE_NOT_FOUND,
            message=f"Service '{service_name}' not found for agent '{agent_id}'",
            field="service_name"
        )

    # Simplify status information
    status_info = {
        "name": service.name,
        "status": service.status.value if hasattr(service.status, 'value') else str(service.status),
        "is_active": getattr(service, 'state_metadata', None) is not None
    }
    
    return ResponseBuilder.success(
        message=f"Service status retrieved for '{service_name}' in agent '{agent_id}'",
        data=status_info
    )

